﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using FFmpegVideoClip.Configuration;
using FFmpegVideoClip.Lib.Afterprocessings;
using FFmpegVideoClip.Lib.ClipGroups;
using FFmpegVideoClip.Mode.Files;
using FFmpegVideoClip.Processing;
using Microsoft.CodeAnalysis;
using UtilityToolsCollect.ObjectsLibrary.ProcessManage;
using UtilityToolsCollect.WinSystemUtilityTools;

namespace FFmpegVideoClip.Lib.AdvancedFragmentProcessor
{
    public class AdvancedTaskFlowManage
    {
        private readonly static object SyncObject = new object();
        public readonly record struct ErrorFragmentElement(Fragment Fragment, int FLowIndex, string ErrorInfo);
        private static int total;
        public static double TotalTIime;
        public static double AfterprocessingTotalTIime;
        public static double AfterprocessingCompletedTimingSeconds;
        public static void StartUP()
        {
            Task.Run(async ()=>
            {
                await Task.Run(() => { });
             });
            AfterprocessingCompletedTimingSeconds = AfterprocessingTotalTIime = 0;
            ClipManage.DuplicateNameCheck();
            ClipManage.ClipListCreateFragmentIndex();
            ClipManage.NormalizationFragmentsID();
            ClipManage.UpdateModeInputIndexValues();
            MainWindow window = (MainWindow)Application.Current.MainWindow;
            foreach (Clip item in ClipManage.ClipList)
            {
                if(item.Flows.Count<1)
                {
                    MessageBoxEx.Show(window, $"剪辑[{item.Name}],未指定处理方案", "空方案错误",MessageBoxButton.OK,MessageBoxImage.Error);
                    return;
                }
            }
            ScriptManage.CompilationScript();
            Fragment[] allFragments = ClipManage.ClipList.Where(p => p.Fragments.Count > 0).SelectMany(p => { p.CompletionStatus = CompletionStatusEnum.NotStart; return p.Fragments; })
                .OrderByDescending(p => p.clip!.IsEnablePriority || (ClipsGroupManage.TryGetGroup(p.clip.Group, out ClipsGroup result) && result.MainClip.IsEnablePriority)).ThenByDescending(p => p.FragmentDuration).ToArray();
            Dictionary<Clip, bool> ClipFragmentStandardProcessComplete = ClipManage.ClipList.ToDictionary(k => k, v => false);
            total = allFragments.Length;
            Dictionary<Fragment, List<AdvancedTaskFlow>> ManageDic = new();
            HashSet<Clip> ErrorClips = new();
            allFragments.ForEach(p =>
            {
                List<AdvancedTaskFlow> FlowList = new List<AdvancedTaskFlow>();
                ManageDic.Add(p, FlowList);

                for (int i = 0; i < p.clip!.Flows.Count; i++)
                {
                    try { FlowList.Add(new AdvancedTaskFlow(p, i, ManageDic)); }
                    catch (Exception e)
                    {
                        if (!ErrorClips.Contains(p.clip))
                            ErrorClips.Add(p.clip);
                        LogManage.WriteExceptionInfo($"[{p.clip.Name}]->{p.Name}->{p.clip!.Flows[i].SchemeName}", e, true);
                    }
                }
            });

            AdvancedTaskFlow[] allFlow = ManageDic.Where(p => p.Key.clip != null && !ErrorClips.Contains(p.Key.clip)).SelectMany(p => p.Value).ToArray();
            TotalTIime = allFlow.Sum(p => p.successAndProgress.TotalSeconds);
            //---------------------------------------------------------------------------
            Dictionary<Clip, AfterprocessingOperates> ClipAfterprocessingOperatesDic = new();
            foreach (Clip item in ClipManage.ClipList.Where(p => p.Fragments.Count > 0 && p.AfterprocessingFlows.Count > 0)) { ClipAfterprocessingOperatesDic.Add(item, new(item)); }
            AfterprocessingTaskFlow[] AllAfterprocessingFlow = ClipAfterprocessingOperatesDic.Values.SelectMany(p => p.taskFlows).ToArray();
            GroupMerge gm = new GroupMerge();
            //---------------------------------------------------------------------------
            LogManage.Clear();
            bool IsErr = false;
            bool IsSuccess = false;
            if (!GroupMerge.ClipGooupModuleArgumentsCheck())
            {
                window.StatusProgressShowError("剪辑组合并中，存在处理过程或参数不一致，详情请在LOG信息中查看");
                return;
            }
            gm.Preparation();

            window.StatusProgressShow();
            window.StatusProgress.Maximum = TotalTIime + gm.TotalSeconds;
            Tools.CurrentCancelToken = new CancellationTokenSource();
            System.Windows.Threading.DispatcherTimer dispatcherTimer = new() { Interval = TimeSpan.FromMilliseconds(1000) };
            dispatcherTimer.Tick += Progress;
            dispatcherTimer.Start();
            SetThreadExecutionStateTools.PreventSleep();
            void Progress(object? sender, EventArgs e)
            {
                if (!IsSuccess && !Tools.IsCancellationRequested)
                {

                    double result = allFlow.Sum(p => p.successAndProgress.CompletedTimingSeconds);
                    AfterprocessingTotalTIime = AllAfterprocessingFlow.Sum(p => p?.successAndProgress.TotalSeconds ?? 0d);
                    AfterprocessingCompletedTimingSeconds = AllAfterprocessingFlow.Sum(p => p?.successAndProgress.CompletedTimingSeconds ?? 0d);
                    window.StatusProgress.Maximum = TotalTIime + AfterprocessingTotalTIime + gm.TotalSeconds;
                    window.StatusProgress.Value = result + AfterprocessingCompletedTimingSeconds + gm.TotalCompletedSeconds;
                    return;
                }
                SetThreadExecutionStateTools.Restore();
                dispatcherTimer.Stop();
                dispatcherTimer.Tick -= Progress;
                dispatcherTimer = null!;
            };
            // Task.Factory.StartNew(() =>
            // {
            //     SetThreadExecutionStateTools.PreventSleep();
            //     while (!IsSuccess && !Tools.IsCancellationRequested)
            //     {
            //         Thread.Sleep(1000);
            //         double result = allFlow.Sum(p => p.successAndProgress.CompletedTimingSeconds);
            //         AfterprocessingTotalTIime = AllAfterprocessingFlow.Sum(p => p?.successAndProgress.TotalSeconds ?? 0d);
            //         AfterprocessingCompletedTimingSeconds = AllAfterprocessingFlow.Sum(p => p?.successAndProgress.CompletedTimingSeconds ?? 0d);
            //         window.Dispatcher.BeginInvoke(() =>
            //         {
            //             window.StatusProgress.Maximum = TotalTIime + AfterprocessingTotalTIime + gm.TotalSeconds;
            //             window.StatusProgress.Value = result + AfterprocessingCompletedTimingSeconds + gm.TotalCompletedSeconds;
            //         });
            //     }
            //     SetThreadExecutionStateTools.Restore();
            // }, Tools.CurrentCancelToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false);
            Task.Factory.StartNew(() =>
            {
                try
                {
                    Parallel.ForEach(ManageDic, new ParallelOptions { MaxDegreeOfParallelism = Config.MaxThread, TaskScheduler = TaskSchedulerSelector.Get, CancellationToken = Tools.CurrentCancelToken!.Token }, CurrentFragment =>
                    {
                        if (Tools.IsCancellationRequested)
                            return;
                        //片段标准流程执行
                        foreach (AdvancedTaskFlow Item in CurrentFragment.Value)
                        {
                            if (Tools.IsCancellationRequested)
                                return;
                            try
                            {
                                Item.Execute();
                            }
                            catch (Exception exc)
                            {
                                LogManage.WriteExceptionInfo($"[{Item.ThisFragment.clip!.Name}]->[{Item.ThisFragment.Name}].{Item.Name}：\r\n{Item.successAndProgress.Log}", exc, true);
                                IsErr = true;
                                //报错后，该片段后面的流程都不跑了，进程全部设置到完成
                                ManageDic[Item.ThisFragment].ForEach(flow =>
                                {
                                    flow.successAndProgress.CompletedTimingSeconds = flow.successAndProgress.TotalSeconds;
                                });
                                return;
                            }
                        }
                        //-------剪辑 合并--------------------------------------------------------------------------------------------
                        try
                        {
                            if (Tools.IsCancellationRequested)
                                return;
                            //获取同剪辑下的片段
                            KeyValuePair<Fragment, List<AdvancedTaskFlow>>[] CurrentFragmentsInClip = ManageDic.Where(kv => kv.Key.clip == CurrentFragment.Key.clip).ToArray();
                            //检查当前片段的剪辑是否全部完成，如完成则进行收尾（移动或合成）
                            if (CurrentFragmentsInClip.All(P_fragment => P_fragment.Value.All(P_Flow => P_Flow.successAndProgress.IsSuccess)))
                            {
                                lock (SyncObject)
                                {
                                    if (ClipFragmentStandardProcessComplete[CurrentFragment.Key.clip!])
                                        return;
                                    ClipFragmentStandardProcessComplete[CurrentFragment.Key.clip!] = true;
                                }
                                //生成输出路径
                                string TarPath = CurrentFragment.Key.clip!.Flows.Last().GetOutput().Parameter;
                                if (!TarPath.StartsWith('.'))
                                    TarPath = "." + TarPath;
                                TarPath = Path.Combine(CurrentFragment.Key.clip.OutDirectory, $"{CurrentFragment.Key.clip.Name}{TarPath}");
                                LogManage.WriteLineNewInfo($"构造输出路径{TarPath}");
                                Tools.CheckDuplicateNameAndLegal(ref TarPath, true);
                                CurrentFragment.Key.clip.FileWritePath = TarPath;

                                //只有一个片段
                                if (CurrentFragmentsInClip.Count() < 2)
                                {

                                    //执行后处理
                                    if (ClipAfterprocessingOperatesDic.ContainsKey(CurrentFragment.Key.clip))
                                    {
                                        AfterprocessingOperates afterprocessing = ClipAfterprocessingOperatesDic[CurrentFragment.Key.clip];
                                        try { afterprocessing.Execute(CurrentFragment.Value.Last().module.GetOutput().Parameter); }
                                        catch (Exception exc) { throw new AfterprocessingOperates.AfterprocessingOperatesException($"[{CurrentFragment.Key.clip}]执行后处理过程错误,执行中断", exc); }
                                        TarPath = afterprocessing.taskFlows.Last().ThisFragment.TempWritePath;
                                        CurrentFragment.Key.clip.FileWritePath = TarPath;
                                        CheckOutFileAndClear(CurrentFragment, TarPath, new AfterprocessingOperates.AfterprocessingOperatesException($"执行后处理失败:[{CurrentFragment.Key.clip.Name}]->{TarPath}"));
                                    }
                                    //直接移动
                                    else
                                    {
                                        if (!Tools.CheckDirectoryPath(Path.GetDirectoryName(TarPath)!, out string m_err))
                                            throw new DirectoryNotFoundException($"目标目录参数异常[{m_err}]");
                                        File.Move(CurrentFragment.Value.Last().module.GetOutput().Parameter, TarPath);
                                        CheckOutFileAndClear(CurrentFragment, TarPath, new FileNotFoundException($"移动失败:[{CurrentFragment.Key.clip.Name}]->{TarPath}"));
                                    }
                                }
                                //多片段
                                else
                                {
                                    string MergeInfoPath = Path.Combine(CurrentFragment.Key.clip!.TempCacheDirectory, $"{CurrentFragment.Key.clip!.Name}_Marge.txt");
                                    StringBuilder sb = new();
                                    //获取所有片段的最后一个流的输出路径并写入合并文档
                                    CurrentFragmentsInClip.OrderBy(p => p.Key.Index).Select(p_fragment => p_fragment.Value.Last().module.GetOutput().Parameter).ForEach(path =>
                                    {
                                        sb.AppendLine($"file '{path}'");
                                    });
                                    File.WriteAllText(MergeInfoPath, sb.ToString());
                                    MergeInfo mi = new();
                                    //后处理
                                    if (ClipAfterprocessingOperatesDic.ContainsKey(CurrentFragment.Key.clip))
                                    {
                                        AfterprocessingOperates afterprocessing = ClipAfterprocessingOperatesDic[CurrentFragment.Key.clip];
                                        try
                                        {
                                            //设置合并路径
                                            TarPath = Path.ChangeExtension(MergeInfoPath, CurrentFragment.Key.clip!.Flows.Last().GetOutput().Parameter);
                                            if (Tools.CheckDuplicateNameAndLegal(ref TarPath, true))
                                                ProcessShell.ProcessExecuteWaitForExit(LosslessSplitTask.FFProcessor, Tools.FFmpegMergeArguments(MergeInfoPath, TarPath), mi.SetTaskInfo, mi.SetTaskInfo);
                                            else throw new ArgumentException($"输出路径参数值[\"{TarPath}\"]非法或根目录不存在");
                                            if (mi.IsError)
                                                throw new MergeException($"[{CurrentFragment.Key.clip.Name}]合并执行异常:详情:\r\n{Tools.FFmpegMergeArguments(MergeInfoPath, TarPath)}\r\n{{{mi}\r\n}}");
                                        }
                                        catch (Exception exc) { throw new MergeException($"[{CurrentFragment.Key.clip.Name}]执行合并过程错误,执行中断", exc); }
                                        try { afterprocessing.Execute(TarPath); }
                                        catch (Exception exc) { throw new AfterprocessingOperates.AfterprocessingOperatesException($"[{CurrentFragment.Key.clip.Name}]执行后处理过程错误,执行中断", exc); }
                                        TarPath = afterprocessing.taskFlows.Last().ThisFragment.TempWritePath;
                                        CurrentFragment.Key.clip.FileWritePath = TarPath;
                                        CheckOutFileAndClear(CurrentFragment, TarPath, new AfterprocessingOperates.AfterprocessingOperatesException($"执行后处理失败:[{CurrentFragment.Key.clip.Name}]->{TarPath}"));
                                    }
                                    //直接合并
                                    else
                                    {
                                        if (!Tools.CheckDirectoryPath(Path.GetDirectoryName(TarPath)!, out string m_err))
                                            throw new DirectoryNotFoundException($"目标目录参数异常[{m_err}]");
                                        ProcessShell.ProcessExecuteWaitForExit(LosslessSplitTask.FFProcessor, Tools.FFmpegMergeArguments(MergeInfoPath, TarPath), mi.SetTaskInfo, mi.SetTaskInfo);
                                        if (mi.IsError)
                                            throw new MergeException($"[{CurrentFragment.Key.clip}]合并执行异常:详情:\r\n{Tools.FFmpegMergeArguments(MergeInfoPath, TarPath)}\r\n{{{mi}\r\n}}");
                                        CheckOutFileAndClear(CurrentFragment, TarPath, new MergeException($"合并失败:[{CurrentFragment.Key.clip.Name}]->{TarPath}"));
                                    }
                                }
                                gm.CheckGroupClipAndExecute();
                            }
                        }
                        catch (Exception exc)
                        {
                            LogManage.WriteExceptionInfo($"合并与后处理失败：[{CurrentFragment.Key.clip!.Name}]", exc, true);
                            CurrentFragment.Key.clip.CompletionStatus = CompletionStatusEnum.Fail;
                            IsErr = true;
                        }
                    });
                    gm.CheckGroupClipAndExecute();
                    gm.MoveSingleFile();
                }
                catch (OperationCanceledException) { IsErr = false; }
                catch (Exception exc)
                {
                    LogManage.WriteExceptionInfo($"[{nameof(AdvancedTaskFlowManage)}] 异常：", exc, true);
                }
                finally
                {
                    IsSuccess = true;

                    window.Dispatcher.BeginInvoke(() =>
                    {
                        if (IsErr)
                        {
                            window.StatusProgressShowError("警告存在错误");
                        }
                        else window.StatusProgressShowComplete("完成");
                    });
                    ErrorClips.Clear();
                    ManageDic.Clear();
                    ClipFragmentStandardProcessComplete.Clear();
                    ClipAfterprocessingOperatesDic.Clear();
                    Tools.CurrentCancelToken!.Dispose();
                    Tools.CurrentCancelToken = null;
                    ScriptManage.ClearScripts();
                    GC.Collect();
                }
            }, Tools.CurrentCancelToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false);
        }

        private static void CheckOutFileAndClear(KeyValuePair<Fragment, List<AdvancedTaskFlow>> CurrentFragment, string TarFilePath, Exception exception)
        {
            CurrentFragment.Key.clip!.CompletionStatus = File.Exists(TarFilePath) ? CompletionStatusEnum.Success : CompletionStatusEnum.Fail;
            if (CurrentFragment.Key.clip.CompletionStatus != CompletionStatusEnum.Success)
                throw exception;
            if (Config.AutoClearCache)
                CurrentFragment.Key.clip.ClearTempCheck();
        }

        [Serializable]
        public class MergeException : Exception
        {
            public MergeException() : base("FFmpeg未能完成合并") { }
            public MergeException(string message) : base($"FFmpeg未能完成合并:{message}") { }
            public MergeException(string message, Exception inner) : base(message, inner) { }
            [Obsolete] protected MergeException(
              System.Runtime.Serialization.SerializationInfo info,
              System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
        }
        public class MergeInfo
        {
            public bool IsError { get; private set; } = true;
            private StringBuilder StringBuilder = new StringBuilder();
            private Regex ProgressExpression;
            SuccessAndProgress? successAndProgress;
            public MergeInfo(SuccessAndProgress? successAndProgress = null)
            {
                ProgressExpression = new(Config.MainCoreProcessor.GetProgressValueExpression, RegexOptions.Compiled);
                this.successAndProgress = successAndProgress;
            }
            public void SetTaskInfo(string? value)
            {
                if (string.IsNullOrWhiteSpace(value))
                    return;
                successAndProgress?.SuccessValidated(value);
                if (Config.FFmpegSuccessValidated.IsMatch(value))
                    IsError = false;
                else if (!string.IsNullOrWhiteSpace(value) && !ProgressExpression.IsMatch(value))
                    StringBuilder.AppendLine(value);
            }
            public override string ToString() => StringBuilder.ToString();
        }
    }

}
